#include "Avalon/WebServices/HttpRequestParser.hpp"
#include <iostream>
#include <boost/algorithm/string/trim.hpp>
#include <boost/lexical_cast.hpp>
#include "Avalon/Base/Algorithm.hpp"
#include "Avalon/WebServices/HttpMethod.hpp"
#include "Avalon/WebServices/HttpServerRequest.hpp"
#include "Avalon/WebServices/InvalidHttpRequestException.hpp"
#include "Avalon/WebServices/Uri.hpp"

using namespace Avalon;
using namespace Avalon::IO;
using namespace Avalon::WebServices;
using namespace boost;
using namespace std;

namespace {
  enum State {
    PARSE_REQUEST,
    PARSE_BODY
  };

  struct MethodInfo {
    const char* m_identifier;
    size_t m_size;
    HttpMethod m_method;
  };
  const MethodInfo methods[] = {
    {"GET ", 4, HttpMethod::GET},
    {"POST ", 5, HttpMethod::POST}};

  bool ExtractRequestMessage(const char** readIterator, int* remainingSize) {
    const void* terminator = memchr(*readIterator, '\r', *remainingSize);
    if(terminator == NULL) {
      return false;
    }
    const char* localReadIterator = *readIterator;
    int localRemainingSize = *remainingSize;
    localRemainingSize -= static_cast<const char*>(terminator) -
      localReadIterator;
    localReadIterator = static_cast<const char*>(terminator);
    if(localRemainingSize < 4) {
      return false;
    }
    if(memcmp(localReadIterator, "\r\n\r\n", 4) != 0) {
      ++localReadIterator;
      --localRemainingSize;
      bool result = ExtractRequestMessage(&localReadIterator,
        &localRemainingSize);
      if(!result) {
        return false;
      }
      *readIterator = localReadIterator;
      *remainingSize = localRemainingSize;
      return true;
    }
    localReadIterator += 4;
    localRemainingSize -= 4;
    *readIterator = localReadIterator;
    *remainingSize = localRemainingSize;
    return true;
  }

  HttpMethod ParseMethod(const char** parserIterator, size_t* remainingSize) {
    const size_t METHOD_SIZE = sizeof(methods) / sizeof(methods[0]);
    for(int i = 0; i < METHOD_SIZE; ++i) {
      const MethodInfo& methodInfo = methods[i];
      if(*remainingSize >= methodInfo.m_size &&
          memcmp(*parserIterator, methodInfo.m_identifier,
          methodInfo.m_size) == 0) {
        *remainingSize -= methodInfo.m_size;
        *parserIterator += methodInfo.m_size;
        return methodInfo.m_method;
      }
    }
    return HttpMethod::NONE;
  }

  Uri ParseUri(const char** parserIterator, size_t* remainingSize) {
    const void* separator = memchr(*parserIterator, ' ', *remainingSize);
    if(separator == NULL) {
      BOOST_THROW_EXCEPTION(MalformedUriException());
    }
    string uriString(*parserIterator, static_cast<int>(
      static_cast<const char*>(separator) - *parserIterator));
    *remainingSize -= static_cast<const char*>(separator) - *parserIterator;
    *parserIterator = static_cast<const char*>(separator) + 1;
    Uri uri(uriString);
    return uri;
  }

  tuple<int, int> ParseVersion(const char** parserIterator,
      size_t* remainingSize) {
    tuple<int, int> version(-1, -1);
    const char* localParserIterator = *parserIterator;
    size_t localRemainingSize = *remainingSize;
    if(localRemainingSize < 5 || memcmp(localParserIterator, "HTTP/", 5) != 0) {
      return version;
    }
    localRemainingSize -= 5;
    localParserIterator += 5;
    if(localRemainingSize < 3) {
      return version;
    }
    if(memcmp(localParserIterator, "1.1", 3) == 0) {
      version = make_tuple(1, 1);
    } else {
      return version;
    }
    const char* end = strstr(localParserIterator, "\r\n") + 2;
    localRemainingSize -= (end - localParserIterator);
    localParserIterator = end;
    *remainingSize = localRemainingSize;
    *parserIterator = localParserIterator;
    return version;
  }

  pair<string, string> ParseHeader(const char** parserIterator,
      size_t* remainingSize) {
    const void* separator = memchr(*parserIterator, ':', *remainingSize);
    if(separator == NULL) {
      return make_pair("", "");
    }
    string name(*parserIterator, static_cast<int>(
      static_cast<const char*>(separator) - *parserIterator));
    *remainingSize -= static_cast<const char*>(separator) - *parserIterator;
    *parserIterator = static_cast<const char*>(separator) + 1;
    const void* lineEnd = strstr(*parserIterator, "\r\n");
    string value(*parserIterator, static_cast<int>(
      static_cast<const char*>(lineEnd) - *parserIterator));
    trim(value);
    *remainingSize -= static_cast<const char*>(lineEnd) - *parserIterator;
    *parserIterator = static_cast<const char*>(lineEnd) + 2;
    return make_pair(name, value);
  }
}

HttpRequestParser::HttpRequestParser()
    : m_state(PARSE_REQUEST) {
  m_readIterator = m_buffer.GetData();
  m_sizeRemaining = 0;
}

HttpRequestParser::~HttpRequestParser() {}

void HttpRequestParser::Feed(const Buffer& data) {
  ptrdiff_t delta = m_readIterator - m_buffer.GetData();
  m_buffer.Append(data);
  m_readIterator = m_buffer.GetData() + delta;
  m_sizeRemaining += data.GetSize();
}

HttpServerRequest* HttpRequestParser::Parse() {
  if(m_state == PARSE_REQUEST) {
    const char* parserIterator = m_readIterator;
    if(!ExtractRequestMessage(&m_readIterator, &m_sizeRemaining)) {
      return NULL;
    }
    size_t remainingRequestSize = m_readIterator - m_buffer.GetData();
    m_method = ParseMethod(&parserIterator, &remainingRequestSize);
    if(m_method == HttpMethod::NONE) {
      BOOST_THROW_EXCEPTION(InvalidHttpRequestException("Invalid method."));
    }
    try {
      m_uri = ParseUri(&parserIterator, &remainingRequestSize);
    } catch(MalformedUriException&) {
      BOOST_THROW_EXCEPTION(InvalidHttpRequestException("Invalid URI."));
    }
    m_version = ParseVersion(&parserIterator, &remainingRequestSize);
    if(m_version.get<0>() == -1) {
      BOOST_THROW_EXCEPTION(
        InvalidHttpRequestException("Invalid HTTP version."));
    }
    while(*parserIterator != '\r') {
      pair<string, string> header = ParseHeader(&parserIterator,
        &remainingRequestSize);
      if(header.first.empty()) {
        BOOST_THROW_EXCEPTION(InvalidHttpRequestException("Invalid header."));
      }
      m_headers.insert(header);

      // TODO
      if(header.first == "Cookie") {
        string::size_type seperator = header.second.find('=');
        if(seperator != string::npos) {
          string name = header.second.substr(0, seperator);
          string value = header.second.substr(seperator + 1);
          Cookie cookie(name, value);
          m_cookies.push_back(cookie);
        }
      }
    }
    m_state = PARSE_BODY;
  }
  if(m_state == PARSE_BODY) {
    map<string, string>::const_iterator contentLengthIterator =
      m_headers.find("Content-Length");
    if(contentLengthIterator != m_headers.end()) {
      int contentLength;
      try {
        contentLength = lexical_cast<int>(contentLengthIterator->second);
      } catch(bad_lexical_cast&) {
        BOOST_THROW_EXCEPTION(
          InvalidHttpRequestException("Invalid Content-Length."));
      }
      if(m_sizeRemaining < contentLength) {
        return NULL;
      }
      m_body.Append(m_readIterator, contentLength);
      m_readIterator += contentLength;
      m_sizeRemaining -= contentLength;
    }
    m_state = PARSE_REQUEST;
    HttpServerRequest* request = new HttpServerRequest(m_version, m_method,
      m_uri, m_headers, m_cookies, m_body);
    m_version = make_tuple(-1, -1);
    m_method = HttpMethod::NONE;
    m_uri = Uri();
    m_headers.clear();
    m_cookies.clear();
    m_body.Reset();
    return request;
  }
  return NULL;
}
